/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkRtspUtil.h"
#include "MkUtil/MkMd5.h"
#include "MkUtil/MkErrorCode.h"
#include "MkUtil/MkLog.h"
#include "MkUtil/MkUtil.h"

CMkRtspUtil CMkRtspUtil::m_Instance;
CMkRtspUtil::CMkRtspUtil()
    : m_bDebugMessageContent(FALSE)
{
    m_MapMethod["DESCRIBE"] = MkRtspMethodDescribe;
    m_MapMethod["ANNOUNCE"] = MkRtspMethodAnnounce;
    m_MapMethod["GET_PARAMETER"] = MkRtspMethodGetParameter;
    m_MapMethod["OPTIONS"] = MkRtspMethodOption;
    m_MapMethod["PAUSE"] = MkRtspMethodPause;
    m_MapMethod["PLAY"] = MkRtspMethodPlay;
    m_MapMethod["RECORD"] = MkRtspMethodRecord;
    m_MapMethod["REDIRECT"] = MkRtspMethodRedirect;
    m_MapMethod["SETUP"] = MkRtspMethodSetup;
    m_MapMethod["SET_PARAMETER"] = MkRtspMethodSetParameter;
    m_MapMethod["TEARDOWN"] = MkRtspMethodTearDown;

    m_MapStatusCode[MkRtspStateContinue] = "Continue";
    m_MapStatusCode[MkRtspStateOk] = "OK";
    m_MapStatusCode[MkRtspStateCreate] = "Created";
    m_MapStatusCode[MkRtspStateLowOnStorageSpace] = "Low on Storage Space";
    m_MapStatusCode[MkRtspStateMultipleChoices] = "Multiple Choices";
    m_MapStatusCode[MkRtspStateMovedPermanently] = "Moved Permanently";
    m_MapStatusCode[MkRtspStateMovedTemporarily] = "Moved Temporarily";
    m_MapStatusCode[MkRtspStateSeeOther] = "See Other";
    m_MapStatusCode[MkRtspStateNotModified] = "Not Modified";
    m_MapStatusCode[MkRtspStateUseProxy] = "Use Proxy";
    m_MapStatusCode[MkRtspStateBadRequest] = "Bad Request";
    m_MapStatusCode[MkRtspStateUnauthorized] = "Unauthorized";
    m_MapStatusCode[MkRtspStatePaymentRequired] = "Payment Required";
    m_MapStatusCode[MkRtspStateForbidden] = "Forbidden";
    m_MapStatusCode[MkRtspStateNotFound] = "Not Found";
    m_MapStatusCode[MkRtspStateMethodNotAllowed] = "Method Not Allowed";
    m_MapStatusCode[MkRtspStateNotAcceptable] = "Not Acceptable";
    m_MapStatusCode[MkRtspStateProxyAuthenticationRequired] = "Proxy Authentication Required";
    m_MapStatusCode[MkRtspStateRequestTimeOut] = "Request Time - out";
    m_MapStatusCode[MkRtspStateGone] = "Gone";
    m_MapStatusCode[MkRtspStateLengthRequired] = "Length Required";
    m_MapStatusCode[MkRtspStatePreconditionFailed] = "Precondition Failed";
    m_MapStatusCode[MkRtspStateRequestEntityTooLarge] = "Request Entity Too Large";
    m_MapStatusCode[MkRtspStateRequestURITooLarge] = "Request - URI Too Large";
    m_MapStatusCode[MkRtspStateUnsupportedCodec] = "Unsupported Media Type";
    m_MapStatusCode[MkRtspStateParameterNotUnderstood] = "Parameter Not Understood";
    m_MapStatusCode[MkRtspStateConferenceNotFound] = "Conference Not Found";
    m_MapStatusCode[MkRtspStateNotEnoughBandwidth] = "Not Enough Bandwidth";
    m_MapStatusCode[MkRtspStateSessionNotFound] = "Session Not Found";
    m_MapStatusCode[MkRtspStateMethodNotValid] = "Method Not Valid in This State";
    m_MapStatusCode[MkRtspStateHeaderFieldNotValid] = "Header Field Not Valid for Resource";
    m_MapStatusCode[MkRtspStateInvalidRange] = "Invalid Range";
    m_MapStatusCode[MkRtspStateParameterIsReadOnly] = "Parameter Is Read - Only";
    m_MapStatusCode[MkRtspStateAggregateOperationNotAllowed] = "Aggregate operation not allowed";
    m_MapStatusCode[MkRtspStateOnlyAggregateOperationAllowed] = "Only aggregate operation allowed";
    m_MapStatusCode[MkRtspStateUnsupportedTransport] = "Unsupported transport";
    m_MapStatusCode[MkRtspStateDestinationUnreachable] = "Destination unreachable";
    m_MapStatusCode[MkRtspStateInternalServerError] = "Internal Server Error";
    m_MapStatusCode[MkRtspStateNotImplemented] = "Not Implemented";
    m_MapStatusCode[MkRtspStateBadGateway] = "Bad Gateway";
    m_MapStatusCode[MkRtspStateServiceUnavailable] = "Service Unavailable";
    m_MapStatusCode[MkRtspStateGatewayTimeOut] = "Gateway Time - out";
    m_MapStatusCode[MkRtspStateRTSPVersionNotSupported] = "RTSP Version not supported";
    m_MapStatusCode[MkRtspStateOptionNotSupported] = "Option not supported";

    m_MapHeader["Accept"] = MkRtspHeaderAccept;
    m_MapHeader["Accept-Encoding"] = MkRtspHeaderAcceptEncoding;
    m_MapHeader["Accept-Language"] = MkRtspHeaderAcceptLanguage;
    m_MapHeader["Allow"] = MkRtspHeaderAllow;
    m_MapHeader["Authorization"] = MkRtspHeaderAuthorization;
    m_MapHeader["Bandwidth"] = MkRtspHeaderBandWidth;
    m_MapHeader["Blocksize"] = MkRtspHeaderBlockSize;
    m_MapHeader["Cache-Control"] = MkRtspHeaderCacheControl;
    m_MapHeader["Conference"] = MkRtspHeaderConference;
    m_MapHeader["Connection"] = MkRtspHeaderConnection;
    m_MapHeader["Content-Base"] = MkRtspHeaderContentBase;
    m_MapHeader["Content-Encoding"] = MkRtspHeaderContentEncoding;
    m_MapHeader["Content-Language"] = MkRtspHeaderContentLanguage;
    m_MapHeader["Content-Length"] = MkRtspHeaderContentLength;
    m_MapHeader["Content-Location"] = MkRtspHeaderContentLocation;
    m_MapHeader["Content-Type"] = MkRtspHeaderContentType;
    m_MapHeader["CSeq"] = MkRtspHeaderCSeq;
    m_MapHeader["Date"] = MkRtspHeaderDate;
    m_MapHeader["Expires"] = MkRtspHeaderExpires;
    m_MapHeader["From"] = MkRtspHeaderFrom;
    m_MapHeader["If-Modified-Since"] = MkRtspHeaderIfModifiedSince;
    m_MapHeader["Last-Modified"] = MkRtspHeaderLastModified;
    m_MapHeader["Proxy-Authenticate"] = MkRtspHeaderProxyAuthenticate;
    m_MapHeader["Proxy-Require"] = MkRtspHeaderProxyRequire;
    m_MapHeader["Public"] = MkRtspHeaderPublic;
    m_MapHeader["Range"] = MkRtspHeaderRange;
    m_MapHeader["Referer"] = MkRtspHeaderReferer;
    m_MapHeader["Require"] = MkRtspHeaderRequire;
    m_MapHeader["Retry-After"] = MkRtspHeaderRetryAfter;
    m_MapHeader["RTP-Info"] = MkRtspHeaderRtpInfo;
    m_MapHeader["Scale"] = MkRtspHeaderScale;
    m_MapHeader["Session"] = MkRtspHeaderSession;
    m_MapHeader["Server"] = MkRtspHeaderServer;
    m_MapHeader["Speed"] = MkRtspHeaderSpeed;
    m_MapHeader["Transport"] = MkRtspHeaderTransport;
    m_MapHeader["Unsupported"] = MkRtspHeaderUnsupported;
    m_MapHeader["User-Agent"] = MkRtspHeaderUserAgent;
    m_MapHeader["Via"] = MkRtspHeaderVia;
    m_MapHeader["WWW-Authenticate"] = MkRtspHeaderWWWAuthenticate;
    m_MapHeader["Location"] = MkRtspHeaderLocation;
    m_MapHeader["Vary"] = MkRtspHeaderVary;

    //m_MapRtspCodec["h264"] = MkCodecH264;
    //m_MapRtspCodec["h265"] = MkCodecH265;
    //m_MapRtspCodec["mpeg4-generic"] = MkCodecAac;
}

const MkString& CMkRtspUtil::GetStatusCodeReason(Uint32 StatusCode)
{
    MkUnorderedMap<Uint32, MkString>::iterator it = m_MapStatusCode.find(StatusCode);
    if (it == m_MapStatusCode.end()) {
        return CMkUtil::GetEmptyString();
    }
    return it->second;
}

const MkString& CMkRtspUtil::GetRtspMethod(const MkRtspMethod& MkMethod)
{
    MkUnorderedMap<MkString, MkRtspMethod>::iterator it = m_MapMethod.begin();
    for (; it != m_MapMethod.end(); it++) {
        if (it->second == MkMethod) {
            return it->first;
        }
    }
    return CMkUtil::GetEmptyString();
}

MkRtspMethod CMkRtspUtil::GetMkMethod(const MkString& strMethod)
{
    MkUnorderedMap<MkString, MkRtspMethod>::iterator it = m_MapMethod.find(strMethod);
    if (it == m_MapMethod.end()) {
        MkErrorLog("unsupported method:%s\n", strMethod.c_str());
        return MkRtspMethodUnknown;
    }
    return it->second;
}

MkRtspHeaderType CMkRtspUtil::GetMkHeaderType(const MkString& Header)
{
    MkUnorderedMap<MkString, MkRtspHeaderType>::iterator it = m_MapHeader.find(Header);
    if (it == m_MapHeader.end()) {
        return MkRtspHeaderUnknown;
    }
    return it->second;
}

const MkString& CMkRtspUtil::GetRtspHeaderType(const MkRtspHeaderType& MkHeader)
{
    MkUnorderedMap<MkString, MkRtspHeaderType>::iterator it = m_MapHeader.begin();
    for (; it != m_MapHeader.end(); it++) {
        if (it->second == MkHeader) {
            return it->first;
        }
    }
    return CMkUtil::GetEmptyString();
}

//MkCodecType CMkRtspUtil::StringCodecToSys(const MkString& strCodecType)
//{
//    MkString TmpCodeType = strCodecType;
//    std::transform(TmpCodeType.begin(), TmpCodeType.end(), TmpCodeType.begin(), ::tolower);
//    MkUnorderedMap<MkString, MkCodecType>::iterator it = m_MapRtspCodec.find(TmpCodeType);
//    if (it != m_MapRtspCodec.end()) {
//        return it->second;
//    }
//    return MkCodecUnknown;
//}
//
//MkString CMkRtspUtil::SysCodecToString(const MkCodecType& Codec)
//{
//    return "";
//}
//
//Uint32 CMkRtspUtil::GetPayload(const MkCodecType& Codec)
//{
//    return 0;
//}

Uint32 CMkRtspUtil::RtspUrlParse(const MkString& RtspUrl, OUT MkString& Host, OUT Uint16& Port,
    OUT MkString& User, OUT MkString& Password, OUT MkString& Path)
{
    size_t nUsed = 0;
    size_t nPos = RtspUrl.find("rtsp://");
    if (MkString::npos == nPos) {
        MkErrorLog("rtsp url:%s not has rtsp://\n", RtspUrl.c_str());
        return InvalidUrl;
    }
    nUsed = strlen("rtsp://");
    nPos = RtspUrl.find("@", nUsed);
    if (MkString::npos != nPos) //has user and password
    {
        nPos = RtspUrl.find(":", nUsed);
        if (MkString::npos == nPos) {
            MkErrorLog("rtspurl:%s check user name failed\n", RtspUrl.c_str());
            return InvalidUrl;
        }
        User = RtspUrl.substr(nUsed, nPos - nUsed);
        nUsed = nPos + 1;
        nPos = RtspUrl.find("@", nUsed);
        if (MkString::npos == nPos) {
            MkErrorLog("rtspurl:%s check user password failed\n", RtspUrl.c_str());
            return InvalidUrl;
        }
        Password = RtspUrl.substr(nUsed, nPos - nUsed);
        nUsed = nPos + 1;
    }
    nPos = RtspUrl.find(":", nUsed);
    size_t nPos2 = RtspUrl.find("/", nUsed);
    if (MkString::npos == nPos2) {
        MkErrorLog("rtspurl:%s not has path\n", RtspUrl.c_str());
        return InvalidUrl;
    }
    if (MkString::npos != nPos) //has port
    {
        Host = RtspUrl.substr(nUsed, nPos - nUsed);
        Port = std::stoi(RtspUrl.substr(nPos + 1, nPos2 - nPos - 1));
    } else {
        Host = RtspUrl.substr(nUsed, nPos2 - nUsed);
        Port = 554;
    }
    nUsed = nPos2;
    Path = RtspUrl.substr(nUsed);
    return NoneError;
}